package gnss

import (
	"dgo/goutils/utils"
	"fmt"
	"io"
	"sort"
	"strings"
	"time"
)

/*
**

	输入Msm, 如果一个历元满了, 就触发OnRtcmMsmEpochEnd事件
*/
type RtcmMsmEpochInput struct {
	curr              *RtcmMsmEpoch
	OnRtcmMsmEpochEnd func(epoch *RtcmMsmEpoch)
}

func NewRtcmMsmEpochInput() *RtcmMsmEpochInput {
	rval := &RtcmMsmEpochInput{}
	return rval
}

func (this *RtcmMsmEpochInput) Close() {
	if this.curr != nil {
		if this.OnRtcmMsmEpochEnd != nil {
			this.OnRtcmMsmEpochEnd(this.curr)
		}
		this.curr = nil
	}
}

func (this *RtcmMsmEpochInput) processEpochEnd() {
	if this.curr.Count() > 0 {
		if this.OnRtcmMsmEpochEnd != nil {
			this.OnRtcmMsmEpochEnd(this.curr)
		}
	}

	this.curr = nil
}

/*
*
返回

	true: 表示一个历元已经完成

要判断一个历元是否完成
add_check_fn: 判断是否需要添加到列表
*/
func (this *RtcmMsmEpochInput) InputMsm(msm *RtcmMsm, add_check_fn func() bool) bool {
	if this.curr != nil && (!msm.MsmTime.IsZero() && this.curr.EpochTime != msm.MsmTime) {
		this.processEpochEnd()
	}

	if this.curr == nil {
		this.curr = NewRtcmMsmEpoch()
	}

	if add_check_fn != nil {
		if add_check_fn() {
			this.curr.AddObs(msm)
		}
	} else {
		this.curr.AddObs(msm)
	}

	if msm.Header.Sync == 0 {
		this.processEpochEnd()
		return true
	}

	return false
}

/**
 * 一个历元的数据, 包含多个系统
 */
type RtcmMsmEpoch struct {
	TOW       uint
	lstMap    map[byte][]*RtcmMsm
	EpochTime time.Time
	n         int
}

func NewRtcmMsmEpoch() *RtcmMsmEpoch {
	rval := &RtcmMsmEpoch{
		lstMap: make(map[byte][]*RtcmMsm),
	}
	return rval
}

func (this *RtcmMsmEpoch) AddObs(msm *RtcmMsm) *RtcmMsm {
	if len(this.lstMap) == 0 {
		this.TOW = msm.Header.TOW
		this.EpochTime = msm.MsmTime
	}
	msmlst := this.lstMap[msm.Sys]
	if msmlst == nil {
		msmlst = make([]*RtcmMsm, 1, 1)
		msmlst[0] = msm
		this.lstMap[msm.Sys] = msmlst
		this.n++
		return msm
	}

	msmlst = append(msmlst, msm)
	this.lstMap[msm.Sys] = msmlst
	this.n++
	return msm
}

/*
*
msm 的数量
*/
func (this *RtcmMsmEpoch) Count() int {
	return this.n
}

func (this *RtcmMsmEpoch) msmToString(msm *RtcmMsm, sb io.Writer) {

	//msm.ToString(sb);
	//return;

	// 只输出信号强度(SNR)
	var i, j byte
	// 循环卫星
	for i = 0; i < msm.Header.SatN; i++ {
		prnId := msm.Header.Sats[i]
		strPrn := GetPrnStrCode(prnId, msm.Sys)
		sb.Write([]byte(strPrn))
		sb.Write(utils.SPACE_BYTES)

		// 循环信号
		for j = 0; j < msm.Header.SigN; j++ {
			iSign := msm.Header.Sigs[j]
			strBrandId := SigiBrandID(msm.Sys, iSign)
			sb.Write([]byte(fmt.Sprintf("%s %.2f ", strBrandId, msm.ObsV[i].Snr[j])))
		}
		sb.Write([]byte("\n"))
	}
}

/*
**

	signHeader: 信号数组
	GNSS Observation Data File – Data Record Description
*/
func (this *RtcmMsmEpoch) msmToRinex3String(msm *RtcmMsm, sb io.Writer, signHeader []byte) int {
	var i byte

	r := 0

	signIdxMap := make(map[byte]byte)

	// 观测值中的信号 创建信号->索引数组
	for x := byte(0); x < msm.Header.SigN; x++ {
		signIdxMap[msm.Header.Sigs[x]] = x + 1
	}

	// 循环卫星
	for i = 0; i < msm.Header.SatN; i++ {
		prnId := msm.Header.Sats[i]
		strPrn := GetPrnStrCode(prnId, msm.Sys)
		sb.Write([]byte(strPrn))
		sb.Write([]byte(" "))
		obsV := msm.ObsV[i] // 一颗卫星的观测值数据
		for j := 0; j < len(signHeader); j++ {
			str := ""
			signV := signHeader[j]
			idx := signIdxMap[signV]
			v1 := float64(0)
			v2 := float64(0)
			v3 := float64(0)
			v4 := float64(0)
			//v5 := uint16(0)
			if idx > 0 {
				idx = idx - 1
				v1 = obsV.PseV[idx]
				v2 = obsV.L[idx]
				v3 = obsV.Doppler[idx]
				v4 = obsV.Snr[idx]
				//v5 = obsV.LTI[idx]
			}

			str += PrintRinexFV(v1, 3, 13) + " "
			str += "  " //LLI SSI
			str += PrintRinexFV(v2, 3, 13) + " "
			str += "  " //LLI SSI
			str += PrintRinexFV(v3, 3, 13) + " "
			str += "  " //LLI SSI
			str += PrintRinexFV(v4, 3, 13) + " "
			str += "  " //LLI SSI
			sb.Write([]byte(str))
		}
		sb.Write([]byte("\n"))

		r++
	}

	return r
}

func (this *RtcmMsmEpoch) Foreach(cb func(sys byte, msm *RtcmMsm)) int {
	r := 0
	msmlst := this.lstMap[SYS_BD]
	if msmlst != nil {
		for _, v := range msmlst {
			cb(SYS_BD, v)
			r++
		}
	}

	msmlst = this.lstMap[SYS_GPS]
	if msmlst != nil {
		for _, v := range msmlst {
			cb(SYS_GPS, v)
			r++
		}
	}

	msmlst = this.lstMap[SYS_GAL]
	if msmlst != nil {
		for _, v := range msmlst {
			cb(SYS_GAL, v)
			r++
		}
	}
	msmlst = this.lstMap[SYS_GLO]
	if msmlst != nil {
		for _, v := range msmlst {
			cb(SYS_GLO, v)
			r++
		}
	}

	msmlst = this.lstMap[SYS_QZS]
	if msmlst != nil {
		for _, v := range msmlst {
			cb(SYS_QZS, v)
			r++
		}
	}
	return r
}

/*
*

	{"Beidou":{"C01":{"B1":44,"B1C":0,"B2":47,"B2a":0,"B3":45.4375}
*/
func (this *RtcmMsmEpoch) writeJSONSNRString(sb *utils.JSONBuilder, lst []*RtcmMsm) {
	if len(lst) == 0 {
		return
	}

	// map[satid]map[sign]snr
	snrMap := make(map[byte]map[byte]float64)
	sign := make([]byte, 0, MAX_SIGNA_N)
	for _, v := range lst {
		sign = v.CheckAddSign(sign)
		v.WriteSNR2Map(snrMap)
	}

	// 写入信号头
	sys := lst[0].Sys
	sb.AddKey(GetNavSysName(sys))
	sb.ObjectBegin()
	n := 0
	for prnId, rec := range snrMap {
		strPrn := GetPrnStrCode(prnId, sys)
		if n > 0 {
			sb.Spliter()
		}
		sb.AddKey(strPrn)
		sb.ObjectBegin()
		j := 0
		for _, sigv := range sign {
			v1 := rec[sigv]
			if v1 != 0 {
				if j > 0 {
					sb.Spliter()
				}
				sb.Add(SigiBrandID(sys, sigv), fmt.Sprintf("%.2f", v1))
				j++
			}
		}
		sb.ObjectEnd()
		n++
	}

	sb.ObjectEnd()
}

func (this *RtcmMsmEpoch) writeSimpleSNRString(sb *strings.Builder, lst []*RtcmMsm) {
	if len(lst) == 0 {
		return
	}

	sign := make([]byte, 0, MAX_SIGNA_N)
	for _, v := range lst {
		sign = v.CheckAddSign(sign)
	}

	// 写入信号头
	sys := lst[0].Sys
	sb.WriteString(GetNavSysName(sys))
	sb.WriteString(";")
	for j := 0; j < len(sign); j++ {
		iSign := byte(sign[j])
		strBrandId := SigiBrandID(sys, iSign)
		if j > 0 {
			sb.WriteString(",")
		}
		sb.WriteString(strBrandId)
	}
	sb.WriteString(";")

	for _, v := range lst {
		v.WriteSimpleSNREx(sb, sign)
	}
}

func (this *RtcmMsmEpoch) writeSNRString(sb *strings.Builder, lst []*RtcmMsm) {

	for _, v := range lst {
		v.WriteBrandSNR(sb)
	}
}

func (this *RtcmMsmEpoch) writeRinex3String(sb io.Writer, lst []*RtcmMsm, signHeader []byte) int {
	r := 0
	for _, v := range lst {
		r += this.msmToRinex3String(v, sb, signHeader)
	}
	return r
}

func (this *RtcmMsmEpoch) lstToMsmString(sb *strings.Builder, lst []*RtcmMsm) {
	for _, v := range lst {
		this.msmToString(v, sb)
	}
}

func (this *RtcmMsmEpoch) SatIDList() string {
	idmap := make(map[string]byte)
	this.Foreach(func(sys byte, msm *RtcmMsm) {
		// 循环卫星
		var i byte
		for i = 0; i < msm.Header.SatN; i++ {
			prnId := msm.Header.Sats[i]
			strPrn := GetPrnStrCode(prnId, msm.Sys)
			idmap[strPrn] = 1
		}
	})

	ids := make([]string, 0, len(idmap))
	for k, _ := range idmap {
		ids = append(ids, k)
	}
	sort.Strings(ids)
	var sb strings.Builder
	for k, v := range ids {
		if k > 0 {
			sb.WriteString(",")
		}
		sb.WriteString(v)
	}
	return sb.String()
}

func (this *RtcmMsmEpoch) StatSNRNum(val float64) int {
	statMap := make(map[byte]map[byte]int)
	for _, msmlst := range this.lstMap {
		if msmlst != nil {
			for _, m := range msmlst {
				m.StatBySNR(statMap, val)
			}
		}
	}

	r := 0
	for _, itm := range statMap {
		r += len(itm)
	}

	return r

}

/*
*
BeiDou:25
GPS:9
GLONASS:9
GALILEO:8
*/
func (this *RtcmMsmEpoch) ToStatSNRString(val float64) string {
	statMap := make(map[byte]map[byte]int)

	var sb strings.Builder
	for _, msmlst := range this.lstMap {
		if msmlst != nil {
			for _, m := range msmlst {
				m.StatBySNR(statMap, val)
			}
		}
	}

	statFunc := func(sys byte) {
		sysMap := statMap[sys]
		if sysMap != nil {
			sb.WriteString(GetNavSysName(sys))
			sb.WriteString(":")
			sb.WriteString(fmt.Sprintf("%d", len(sysMap)))
			sb.WriteString("\n")
		}
	}

	statFunc(SYS_BD)
	statFunc(SYS_GPS)
	statFunc(SYS_GLO)
	statFunc(SYS_GAL)
	return sb.String()
}

/*
*
// {"BeiDou":{"C01":{"B1":44,"B1C":0,"B2":47,"B2a":0,"B3":45.4375}...
*/
func (this *RtcmMsmEpoch) ToJSONSNRString() string {
	var sb utils.JSONBuilder
	n := 0
	var checkFunc = func() {
		if n == 0 {
			sb.ObjectBegin()
		} else {
			sb.Spliter()
		}
	}
	msmlst := this.lstMap[SYS_BD]
	if msmlst != nil {
		checkFunc()
		this.writeJSONSNRString(&sb, msmlst)
		n++
	}

	msmlst = this.lstMap[SYS_GPS]
	if msmlst != nil {
		checkFunc()
		this.writeJSONSNRString(&sb, msmlst)
		n++
	}
	//
	msmlst = this.lstMap[SYS_GAL]
	if msmlst != nil {
		checkFunc()
		this.writeJSONSNRString(&sb, msmlst)
		n++
	}
	//
	msmlst = this.lstMap[SYS_GLO]
	if msmlst != nil {
		checkFunc()
		this.writeJSONSNRString(&sb, msmlst)
		n++
	}
	if n > 0 {
		sb.ObjectEnd()
	}
	return sb.String()
}

/*
*
BeiDou;B1,B1C,B3,B2,B2a;C01|43.75,0.00,43.50,46.12,0.00;C02|42.31,0.00,44.69,49.06,0.00;
*/
func (this *RtcmMsmEpoch) ToSimpleSNRString() string {
	var sb strings.Builder
	msmlst := this.lstMap[SYS_BD]
	if msmlst != nil {
		this.writeSimpleSNRString(&sb, msmlst)
		sb.WriteString("\n")
	}

	msmlst = this.lstMap[SYS_GPS]
	if msmlst != nil {
		this.writeSimpleSNRString(&sb, msmlst)
		sb.WriteString("\n")
	}

	msmlst = this.lstMap[SYS_GAL]
	if msmlst != nil {
		this.writeSimpleSNRString(&sb, msmlst)
		sb.WriteString("\n")
	}

	msmlst = this.lstMap[SYS_GLO]
	if msmlst != nil {
		this.writeSimpleSNRString(&sb, msmlst)
		sb.WriteString("\n")
	}
	return sb.String()
}

/*
*
G04 L1 44.44 L2 42.00 L2 44.94 L5 48.38
G08 L1 46.06 L2 47.81 L2 45.06 L5 47.19
G09 L1 39.06 L2 41.94 L2 37.88 L5 42.75
G16 L1 44.06 L2 41.19 L2 0.00 L5 0.00
G22 L1 40.06 L2 37.00 L2 0.00 L5 0.00
*/
func (this *RtcmMsmEpoch) ToSNRString() string {
	var sb strings.Builder
	msmlst := this.lstMap[SYS_BD]
	if msmlst != nil {
		this.writeSNRString(&sb, msmlst)
	}

	msmlst = this.lstMap[SYS_GPS]
	if msmlst != nil {
		this.writeSNRString(&sb, msmlst)
	}

	msmlst = this.lstMap[SYS_GAL]
	if msmlst != nil {
		this.writeSNRString(&sb, msmlst)
	}

	msmlst = this.lstMap[SYS_GLO]
	if msmlst != nil {
		this.writeSNRString(&sb, msmlst)
	}
	return sb.String()
}

func (this *RtcmMsmEpoch) WriteRinex3String(signHeader *RinexHeader, sb io.Writer) int {
	r := 0
	lst := this.lstMap[SYS_BD]
	if lst != nil {
		r += this.writeRinex3String(sb, lst, signHeader.CHeader)
	}

	lst = this.lstMap[SYS_GPS]
	if lst != nil {
		r += this.writeRinex3String(sb, lst, signHeader.GHeader)
	}

	lst = this.lstMap[SYS_GAL]
	if lst != nil {
		r += this.writeRinex3String(sb, lst, signHeader.EHeader)
	}

	lst = this.lstMap[SYS_GLO]
	if lst != nil {
		r += this.writeRinex3String(sb, lst, signHeader.GloHeader)
	}

	lst = this.lstMap[SYS_QZS]
	if lst != nil {
		r += this.writeRinex3String(sb, lst, signHeader.JHeader)
	}
	return r
}

func (this *RtcmMsmEpoch) ToRinex3String(signHeader *RinexHeader) (int, string) {
	var sb strings.Builder
	r := this.WriteRinex3String(signHeader, &sb)
	return r, sb.String()
}

func (this *RtcmMsmEpoch) ToBNCString(siteid string) string {
	var sb utils.BytesBuilder
	week, secs := ToGPSTime(this.EpochTime)
	str := fmt.Sprintf("> %d %.7f %.2f ms %s\n", week, secs, GpsTimeDelaySecs(this.EpochTime), utils.DateTimeString(time.Now()))
	sb.AppendStr(str)
	this.Foreach(func(sys byte, msm *RtcmMsm) {
		msm.ToBNCString(siteid, &sb)
	})
	return sb.String()
}

func (this *RtcmMsmEpoch) SatNumString() string {
	var sb strings.Builder
	var sys0 byte
	var n int = 0
	var j int = 0
	this.Foreach(func(sys byte, msm *RtcmMsm) {
		if sys0 == 0 {
			sys0 = sys
		}
		if sys0 != sys {
			sb.WriteString(fmt.Sprintf("%s:%d %d; ", GNSS_SYS_RINEX_CHAR[sys0], n, j))
			n = 0
			j = 0
			sys0 = sys
		}
		n += int(msm.Header.SatN)
		j++
	})

	if sys0 != 0 && n > 0 {
		sb.WriteString(fmt.Sprintf("%s:%d %d; ", GNSS_SYS_RINEX_CHAR[sys0], n, j))
	}

	return sb.String()
}

func (this *RtcmMsmEpoch) String() string {
	var sb strings.Builder
	lst := this.lstMap[SYS_BD]
	if lst != nil {
		this.lstToMsmString(&sb, lst)
	}

	lst = this.lstMap[SYS_GPS]
	if lst != nil {
		this.lstToMsmString(&sb, lst)
	}

	lst = this.lstMap[SYS_GLO]
	if lst != nil {
		this.lstToMsmString(&sb, lst)
	}

	lst = this.lstMap[SYS_GAL]
	if lst != nil {
		this.lstToMsmString(&sb, lst)
	}
	return sb.String()
}

func (this *RtcmMsmEpoch) SaveToFile(filename string) error {
	var sb utils.BytesBuilder
	this.Save2Bytes(&sb)

	return sb.SaveToFile(filename)
}

func (this *RtcmMsmEpoch) SaveToHexString() string {
	var sb utils.BytesBuilder
	this.Save2Bytes(&sb)

	return sb.HexString("")
}

func (this *RtcmMsmEpoch) Save2Bytes(io io.Writer) {
	io.Write(utils.Int64_BEBytes(this.EpochTime.Unix()))

	var sb utils.BytesBuilder
	var n byte = 0
	this.Foreach(func(sys byte, msm *RtcmMsm) {
		rawBuf := msm.RawBuf
		if len(rawBuf) > 0 {
			sb.Write(utils.UInt16_BEBytes(uint16(len(rawBuf))))
			sb.Write(msm.RawBuf)
			n++
		}
	})
	if n > 0 {
		io.Write([]byte{n})
		io.Write(sb.Bytes())
	}
}

func (this *RtcmMsmEpoch) LoadFromBytes(io io.Reader) error {
	buf := make([]byte, 8)
	err := utils.ReadFullBuf(io, buf)
	if err != nil {
		return err
	}
	ts := utils.Int64_FromBEBytes(buf)
	t1 := time.Unix(ts, 0)
	this.EpochTime = t1

	err = utils.ReadLenBuf(io, buf, 1)
	if err != nil {
		return err
	}

	n := buf[0]
	if n == 0 {
		return nil
	}

	for {
		if n == 0 {
			break
		}

		l, err := utils.ReadUInt16BE(io)
		if err != nil {
			return err
		}
		rawBuf := make([]byte, l, l)
		err = utils.ReadFullBuf(io, rawBuf)
		if err != nil {
			return err
		}

		msm, _ := DecodeRtcmMsm(rawBuf, 24)
		msm.MsmTime = msm.Header.DecodeTime(this.EpochTime)
		if msm != nil {
			msm.RawBuf = rawBuf
			msm.ProcObs()
			this.AddObs(msm)
		}
		n--
	}

	return nil

}
